package chconfig

import (
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/common/options"
	contextImpl "git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/ichain/context"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/ichain"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/util/concurrent/lazyref"
	"github.com/pkg/errors"
)

type Ref struct {
	*lazyref.Reference
	pvdr       Provider
	ctx        ichain.ClientContext
	chainID    string
	errHandler ichain.ErrorHandler
}

type ChainConfigError error

func NewRef(ctx ichain.ClientContext, pvdr Provider, chain string, opts ...options.Opt) *Ref {
	params := newDefaultParams()
	options.Apply(params, opts)

	cfgRef := &Ref{
		pvdr:       pvdr,
		ctx:        ctx,
		chainID:    chain,
		errHandler: params.errHandler,
	}

	cfgRef.Reference = lazyref.New(
		cfgRef.initializer(),
		lazyref.WithRefreshInterval(lazyref.InitImmediately, params.refreshInterval),
	)

	return cfgRef
}

func (ref *Ref) initializer() lazyref.Initializer {
	return func() (interface{}, error) {
		chConfig, err := ref.getConfig()
		if err != nil && ref.errHandler != nil {
			logger.Debugf("[%s] An error occurred while retrieving chain config. Invoking error handler.", ref.chainID)
			ref.errHandler(ref.ctx, ref.chainID, ChainConfigError(err))
		}
		return chConfig, err
	}
}

func (ref *Ref) getConfig() (ichain.ChainCfg, error) {
	chConfigProvider, err := ref.pvdr(ref.chainID)
	if err != nil {
		return nil, errors.WithMessage(err, "error creating chain config provider")
	}

	reqCtx, cancel := contextImpl.NewRequest(ref.ctx, contextImpl.WithTimeoutType(ichain.InodeResponse))
	defer cancel()

	chConfig, err := chConfigProvider.Query(reqCtx)
	if err != nil {
		return nil, err
	}

	return chConfig, nil
}
